#pragma once

//#define DRAW_POINTS				0x01
//#define DRAW_EDGES              0x02
//#define DRAW_TRIANGLES          0x04
//#define DRAW_NORMALS            0x08
#include <QtGui/QVector3D>
//class QVector3D;
class QString;

namespace CGF{
class CTriMesh 
{

public:
	///////////////////////////////////////////////////////////////////////
	// Local classes definition

	enum DrawType {DRAW_POINTS, DRAW_EDGES, DRAW_TRIANGLES, DRAW_NORMALS};

	// Material information class
	class CMaterial
	{
	public:
		char  m_sName[256];			// name of material 
		float m_fDiffuse[4];		// diffuse component 
		float m_fAmbient[4];		// ambient component 
		float m_fSpecular[4];		// specular component 
		float m_fEmissive[4];		// emissive component 
		float m_fShininess[1];		// specular exponent 

		char * MaterialName() {return (m_sName);}
		float * DiffuseComponent() {return (m_fDiffuse);}
		float * AmbientComponent() {return (m_fAmbient);}
		float * SpecularComponent() {return (m_fSpecular);}
		float * EmissiveComponent() {return (m_fEmissive);}
		float * ShininessComponent() {return (m_fShininess);}

		void SetMaterialName(char* name)
		{
			strcpy_s(m_sName, sizeof(m_sName), name);
		}
		void SetDiffuseComponent(float* diffuse)
		{
			m_fDiffuse[0] = diffuse[0];
			m_fDiffuse[1] = diffuse[1];
			m_fDiffuse[2] = diffuse[2];
			m_fDiffuse[3] = diffuse[3];
		}
		void SetAmbientComponent(float* ambient)
		{
			m_fAmbient[0] = ambient[0];
			m_fAmbient[1] = ambient[1];
			m_fAmbient[2] = ambient[2];
			m_fAmbient[3] = ambient[3];
		}
		void SetSpecularComponent(float* specular)
		{
			m_fSpecular[0] = specular[0];
			m_fSpecular[1] = specular[1];
			m_fSpecular[2] = specular[2];
			m_fSpecular[3] = specular[3];
		}
		void SetEmissiveComponent(float* emissive)
		{
			m_fEmissive[0] = emissive[0];
			m_fEmissive[1] = emissive[1];
			m_fEmissive[2] = emissive[2];
			m_fEmissive[3] = emissive[3];
		}
		void SetShininessComponent(float* shininess)
		{	
			m_fShininess[0] = shininess[0];
		}

		CMaterial()
		{
			sprintf_s (m_sName, sizeof(m_sName), "default");
			m_fDiffuse[0] = m_fDiffuse[1] = m_fDiffuse[2] = m_fDiffuse[3] = 1.0f;
			m_fAmbient[0] = m_fAmbient[1] = m_fAmbient[2] = m_fAmbient[3] = 0.1f;
			m_fSpecular[0] = m_fSpecular[1] = m_fSpecular[2] = m_fSpecular[3] = 0.0f;
			m_fEmissive[0] = m_fEmissive[1] = m_fEmissive[2] = m_fEmissive[3] = 0.0f;
			m_fShininess[0] = 10;
		}

		virtual ~CMaterial() {}
	};
	// --------------------------------------------------------------------
	// Vertex information class
	class CVertex
	{
	public:
		QVector3D	m_vPosition;					// world coordinate of the vertex
		QVector3D	m_vNormal;						// normal of this vertex

		int			m_nTriangle;					// number of triangles that share this vertex
		int *		m_pTriangleIndices;				// link of triangles that share this vertex

		int			m_nNeighbor;					// neighbor vertices number
		int *		m_npNeighborVertexIndices;		// indices of neighbor vertices

		int			m_nFlag;						// flag

		QVector3D   m_vT1;
		QVector3D   m_vT2;
		float       m_k1;
		float       m_k2;

		CVertex()
		{
			m_nTriangle = 0;
			m_pTriangleIndices = NULL;

			m_nNeighbor = 0;
			m_npNeighborVertexIndices = NULL;

			m_nFlag = -1;
		}
		virtual ~CVertex()
		{
			if (m_nTriangle != 0)
				delete [] m_pTriangleIndices;
			if (m_nNeighbor != 0)
				delete [] m_npNeighborVertexIndices;

		}
	protected:
	};

	// --------------------------------------------------------------------
	// Edge information class
	class CEdge
	{
	public:
		int	m_nVertexIndices[2];		// the two vertices that define this edge
		int	m_nTriangleIndices[2];		// the two triangles that share this edge
		int	m_nFlag;					// flag

		//
		// construction and destruction
		//
		CEdge()	
		{
			m_nVertexIndices[0] = m_nVertexIndices[1] = -1; 
			m_nTriangleIndices[0] = m_nTriangleIndices[1] = -1;
			m_nFlag = 0;
		}
		virtual ~CEdge() {}
	};

	// --------------------------------------------------------------------
	// Triangle information class
	class CTriangle
	{
	public:
		int			m_nVertexIndices[3];		// indices of the three vertices in vertex link
		QVector3D	m_vTexture[3];				// texture coordinates of the three vertices

		int			m_nEdgeIndices[3];			// indices of the three edges of this triangle
		int			m_nFlag;

		QVector3D	m_vCenter;					// center of this triangle
		QVector3D	m_vN;						// normal of this triangle	

	public:

		CTriangle()
		{
			m_nVertexIndices[0] = m_nVertexIndices[1] = m_nVertexIndices[2] = -1;
			m_nEdgeIndices[0] = m_nEdgeIndices[1] = m_nEdgeIndices[2] = -1;
			m_vTexture[0] = m_vTexture[1] = m_vTexture[2] = QVector3D(0, 0, 0);

			m_vCenter = QVector3D(0, 0, 0);
			m_vN = QVector3D(0, 0, 0);
			m_nFlag = 0;

		}
		virtual ~CTriangle() 
		{

		}

	};

	//////////////////////////////////////////////////////////////////////////
	// Triangle mesh scene attributes

public:

	// --------------------------------------------------------------------
	// Variables
	int				m_nMaterials;			// number materials
	CMaterial *		m_pMaterials;			// materials link
	int				m_nVertices;			// number of vertices
	CVertex *		m_pVertices;			// vertices link
	int				m_nEdges;				// number of edges
	CEdge *			m_pEdges;				// edges link
	int				m_nTriangles;			// number of triangles
	CTriangle *		m_pTriangles;			// triangles link

	QVector3D		m_vMax, m_vMin;			// bounding box of the scene

public:
	DrawType		m_nRenderingStyles;		// different draw type
	QString			m_sOBJFilename;			// Wavefront obj file name

protected:
	virtual void Destory();						

	void BuildTopologyAdjacency();
	void BuildVertexNbrTriangle();
	void BuildVertexNbrVertex();
	void BuildEdges();
	void ComputeTriangle_Vertex_Normal(bool bVn);
	void UnifyModel();

protected:
	void SetMaterial();

	void DrawSceneByPoints();
	void DrawSceneByEdges();
	void DrawSceneByTriangles();

	void ShowWireFrame();
	void ShowCoordinate();

public:
	CTriMesh();
	virtual ~CTriMesh();

	bool LoadOBJModel(QString sFilename);
	void SaveOBJModel(QString sFilename);

	void UnifyOrientation();
	void InverseTriOrient();
	void GetBoundingBox(QVector3D &vMax, QVector3D &vMin);

	void SetRenderingStyles(DrawType nStyles);
	void DrawScene();

	int SelectOnePoint(QPoint point, bool isSelFromPC, float* M=NULL);


	void Delaunay(bool Delaunay);
	//void Sampling(int nSample, bool Adaptive, int dt_ite_n, int ite_n);
	//void InitSampling(int nSamples, CTriMesh* SampleMesh);
	//void Remeshing();
	void CalCurv();
	//float CalAreaSquare(float *);
	//void SpectralAnalysis();
};

}